﻿// -----------------------------------------------------------------------
// <copyright company="www.3WayWebdesign.de">
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
// 
// <author>Michael Morbach</author>
// <email>Michael@3waywebdesign.de</email>
// <web>http://www.3waywebdesign.de</web>
// ----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Tww.Wpf.Controls.Controls.Flyouts;
using Tww.Wpf.Controls.Controls.Shared.Enums;
using Tww.Wpf.Controls.Controls.Shared.Helper;
using Tww.Wpf.Controls.Native;
using SystemCommands = Microsoft.Windows.Shell.SystemCommands;

namespace Tww.Wpf.Controls.Controls.Windows
{

    #region Imports

    #endregion

    [TemplatePart(Name = PART_OverlayBox, Type = typeof (Grid))]
    [TemplatePart(Name = PART_Icon, Type = typeof (UIElement))]
    [TemplatePart(Name = PART_TitleBar, Type = typeof (UIElement))]
    [TemplatePart(Name = PART_WindowTitleBackground, Type = typeof (UIElement))]
    [TemplatePart(Name = PART_LeftWindowCommands, Type = typeof (WindowCommands))]
    [TemplatePart(Name = PART_RightWindowCommands, Type = typeof (WindowCommands))]
    [TemplatePart(Name = PART_WindowButtonCommands, Type = typeof (WindowButtonCommands))]
    [TemplatePart(Name = PART_FlyoutModal, Type = typeof (Rectangle))]
    public class MetroWindow : Window
    {
        #region  - Felder (öffentlich) -
        
        public static readonly DependencyProperty CloseButtonVisibilityProperty =
            DependencyProperty.Register("CloseButtonVisibility", typeof (Visibility), typeof (MetroWindow),
                new PropertyMetadata(Visibility.Visible));

        public static readonly DependencyProperty EnableDWMDropShadowProperty =
            DependencyProperty.Register("EnableDWMDropShadow", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(false));

        public static readonly DependencyProperty FlyoutsProperty = DependencyProperty.Register("Flyouts",
            typeof (FlyoutContainer), typeof (MetroWindow), new PropertyMetadata(null));

        public static readonly RoutedEvent FlyoutsStatusChangedEvent = EventManager.RegisterRoutedEvent(
            "FlyoutsStatusChanged", RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (MetroWindow));

        public static readonly DependencyProperty GlowBrushProperty = DependencyProperty.Register("GlowBrush",
            typeof (SolidColorBrush), typeof (MetroWindow), new PropertyMetadata(null));

        public static readonly DependencyProperty IconBitmapScalingModeProperty =
            DependencyProperty.Register("IconBitmapScalingMode", typeof (BitmapScalingMode), typeof (MetroWindow),
                new PropertyMetadata(BitmapScalingMode.HighQuality));

        public static readonly DependencyProperty IconEdgeModeProperty = DependencyProperty.Register("IconEdgeMode",
            typeof (EdgeMode), typeof (MetroWindow), new PropertyMetadata(EdgeMode.Aliased));

        public static readonly DependencyProperty IconOnTitleBarVisibilityProperty =
            DependencyProperty.Register("IconOnTitleBarVisibility", typeof (Visibility), typeof (MetroWindow),
                new PropertyMetadata(Visibility.Visible));

        public static readonly DependencyProperty IconTemplateProperty = DependencyProperty.Register("IconTemplate",
            typeof (DataTemplate), typeof (MetroWindow), new PropertyMetadata(null));

        public static readonly DependencyProperty IgnoreTaskbarOnMaximizeProperty =
            DependencyProperty.Register("IgnoreTaskbarOnMaximize", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(false));

        public static readonly DependencyProperty IsWindowTransitionsEnabledProperty =
            DependencyProperty.Register("IsWindowTransitionsEnabled", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(true));

        public static readonly DependencyProperty LeftWindowCommandsProperty =
            DependencyProperty.Register("LeftWindowCommands", typeof (WindowCommands), typeof (MetroWindow),
                new PropertyMetadata(null));

        public static readonly DependencyProperty MaxRestoreButtonVisibilityProperty =
            DependencyProperty.Register("MaxRestoreButtonVisibility", typeof (Visibility), typeof (MetroWindow),
                new PropertyMetadata(Visibility.Visible));

        public static readonly DependencyProperty MinButtonVisibilityProperty =
            DependencyProperty.Register("MinButtonVisibility", typeof (Visibility), typeof (MetroWindow),
                new PropertyMetadata(Visibility.Visible));

        public static readonly DependencyProperty NonActiveBorderBrushProperty =
            DependencyProperty.Register("NonActiveBorderBrush", typeof (Brush), typeof (MetroWindow),
                new PropertyMetadata(Brushes.Gray));

        public static readonly DependencyProperty NonActiveGlowBrushProperty =
            DependencyProperty.Register("NonActiveGlowBrush", typeof (SolidColorBrush), typeof (MetroWindow),
                new PropertyMetadata(new SolidColorBrush(Color.FromRgb(153, 153, 153))));

        public static readonly DependencyProperty NonActiveWindowTitleBrushProperty =
            DependencyProperty.Register("NonActiveWindowTitleBrush", typeof (Brush), typeof (MetroWindow),
                new PropertyMetadata(Brushes.Gray));

        public static readonly DependencyProperty OverrideDefaultWindowCommandsBrushProperty =
            DependencyProperty.Register("OverrideDefaultWindowCommandsBrush", typeof (SolidColorBrush),
                typeof (MetroWindow));

        public static readonly DependencyProperty RightWindowCommandsProperty =
            DependencyProperty.Register("RightWindowCommands", typeof (WindowCommands), typeof (MetroWindow),
                new PropertyMetadata(null));

        public static readonly DependencyProperty SaveWindowPositionProperty =
            DependencyProperty.Register("SaveWindowPosition", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(false));

        public static readonly DependencyProperty ShowWindowCommandsOnTopProperty =
            DependencyProperty.Register("ShowWindowCommandsOnTop", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(true));

        public static readonly DependencyProperty TitleBarVisibilityProperty =
            DependencyProperty.Register("TitleBarVisibility", typeof (Visibility), typeof (MetroWindow),
                new PropertyMetadata(Visibility.Visible, OnTitleBarVisibilityPropertyChangedCallback,
                    OnTitleBarVisibilityCoerceValueCallback));

        public static readonly DependencyProperty TitleCapsProperty = DependencyProperty.Register("TitleCaps",
            typeof (bool), typeof (MetroWindow), new PropertyMetadata(true));

        public static readonly DependencyProperty TitleForegroundProperty =
            DependencyProperty.Register("TitleForeground", typeof (Brush), typeof (MetroWindow));

        public static readonly DependencyProperty TitleIconVisibilityProperty =
            DependencyProperty.Register("TitleIconVisibility", typeof (Visibility), typeof (MetroWindow),
                new PropertyMetadata(Visibility.Visible));

        public static readonly DependencyProperty WindowTitleVisibilityProperty =
            DependencyProperty.Register("WindowTitleVisibility", typeof(Visibility), typeof(MetroWindow),
                new PropertyMetadata(Visibility.Visible));

        public static readonly DependencyProperty TitleTemplateProperty = DependencyProperty.Register("TitleTemplate",
            typeof (DataTemplate), typeof (MetroWindow), new PropertyMetadata(null));

        public static readonly DependencyProperty TitlebarHeightProperty = DependencyProperty.Register(
            "TitlebarHeight", typeof (int), typeof (MetroWindow),
            new PropertyMetadata(30, TitlebarHeightPropertyChangedCallback));

        public static readonly DependencyProperty UseNoneWindowStyleProperty =
            DependencyProperty.Register("UseNoneWindowStyle", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(false, OnUseNoneWindowStylePropertyChangedCallback));

        public static readonly DependencyProperty WindowCloseButtonStyleProperty =
            DependencyProperty.Register("WindowCloseButtonStyle", typeof (Style), typeof (MetroWindow),
                new PropertyMetadata(null));

        public static readonly DependencyProperty WindowCommandsVisibilityProperty =
            DependencyProperty.Register("WindowCommandsVisibility", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(true));

        public static readonly DependencyProperty WindowMaxButtonStyleProperty =
            DependencyProperty.Register("WindowMaxButtonStyle", typeof (Style), typeof (MetroWindow),
                new PropertyMetadata(null));

        public static readonly DependencyProperty WindowMinButtonStyleProperty =
            DependencyProperty.Register("WindowMinButtonStyle", typeof (Style), typeof (MetroWindow),
                new PropertyMetadata(null));

        public static readonly DependencyProperty WindowPlacementSettingsProperty =
            DependencyProperty.Register("WindowPlacementSettings", typeof (IWindowPlacementSettings),
                typeof (MetroWindow), new PropertyMetadata(null));

        public static readonly DependencyProperty WindowTitleBrushProperty =
            DependencyProperty.Register("WindowTitleBrush", typeof (Brush), typeof (MetroWindow),
                new PropertyMetadata(Brushes.Transparent));

        public static readonly DependencyProperty WindowTransitionsEnabledProperty =
            DependencyProperty.Register("WindowTransitionsEnabled", typeof (bool), typeof (MetroWindow),
                new PropertyMetadata(true));

        #endregion

        #region  - Felder (privat) -

        internal ContentPresenter LeftWindowCommandsPresenter;
        internal ContentPresenter RightWindowCommandsPresenter;
        internal WindowButtonCommands WindowButtonCommands;
        private Rectangle flyoutModal;

        private UIElement icon;
        private bool isDragging;
        internal Grid OverlayBox;
        private Storyboard overlayStoryboard;
        private UIElement titleBar;
        private UIElement titleBarBackground;

        #endregion

        #region  - Konstruktoren -

        static MetroWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof (MetroWindow),
                new FrameworkPropertyMetadata(typeof (MetroWindow)));
        }

        #endregion

        #region  - Konstruktoren -

        /// <summary>
        ///     Initializes a new instance of the MahApps.Metro.Controls.MetroWindow class.
        /// </summary>
        public MetroWindow()
        {
            Loaded += MetroWindow_Loaded;
        }

        #endregion

        #region  - Indexer und Eigenschaften (öffentlich) -

        /// <summary>
        ///     Gets/sets the brush used for the Window's non-active glow.
        /// </summary>
        public SolidColorBrush NonActiveGlowBrush
        {
            get { return (SolidColorBrush) GetValue(NonActiveGlowBrushProperty); }
            set { SetValue(NonActiveGlowBrushProperty, value); }
        }

        /// <summary>
        ///     Gets/sets bitmap scaling mode of the titlebar icon.
        /// </summary>
        public BitmapScalingMode IconBitmapScalingMode
        {
            get { return (BitmapScalingMode) GetValue(IconBitmapScalingModeProperty); }
            set { SetValue(IconBitmapScalingModeProperty, value); }
        }

        /// <summary>
        ///     Gets/sets edge mode of the titlebar icon.
        /// </summary>
        public EdgeMode IconEdgeMode
        {
            get { return (EdgeMode) GetValue(IconEdgeModeProperty); }
            set { SetValue(IconEdgeModeProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the style for the MIN button style.
        /// </summary>
        public Style WindowMinButtonStyle
        {
            get { return (Style) GetValue(WindowMinButtonStyleProperty); }
            set { SetValue(WindowMinButtonStyleProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the style for the MAX button style.
        /// </summary>
        public Style WindowMaxButtonStyle
        {
            get { return (Style) GetValue(WindowMaxButtonStyleProperty); }
            set { SetValue(WindowMaxButtonStyleProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the style for the CLOSE button style.
        /// </summary>
        public Style WindowCloseButtonStyle
        {
            get { return (Style) GetValue(WindowCloseButtonStyleProperty); }
            set { SetValue(WindowCloseButtonStyleProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the brush used for the Window's title bar.
        /// </summary>
        public Brush WindowTitleBrush
        {
            get { return (Brush) GetValue(WindowTitleBrushProperty); }
            set { SetValue(WindowTitleBrushProperty, value); }
        }

        public IWindowPlacementSettings WindowPlacementSettings
        {
            get { return (IWindowPlacementSettings) GetValue(WindowPlacementSettingsProperty); }
            set { SetValue(WindowPlacementSettingsProperty, value); }
        }

        /// <summary>
        ///     Gets/sets whether the window's entrance transition animation is enabled.
        /// </summary>
        public bool WindowTransitionsEnabled
        {
            get { return (bool) GetValue(WindowTransitionsEnabledProperty); }
            set { SetValue(WindowTransitionsEnabledProperty, value); }
        }

        /// <summary>
        ///     Gets/sets whether the window will save it's position between loads.
        /// </summary>
        public bool SaveWindowPosition
        {
            get { return (bool) GetValue(SaveWindowPositionProperty); }
            set { SetValue(SaveWindowPositionProperty, value); }
        }

        public bool EnableDWMDropShadow
        {
            get { return (bool) GetValue(EnableDWMDropShadowProperty); }
            set { SetValue(EnableDWMDropShadowProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the brush used for the Window's glow.
        /// </summary>
        public SolidColorBrush GlowBrush
        {
            get { return (SolidColorBrush) GetValue(GlowBrushProperty); }
            set { SetValue(GlowBrushProperty, value); }
        }


        /// <summary>
        ///     Gets/sets whether the window will ignore (and overlap) the taskbar when maximized.
        /// </summary>
        public bool IgnoreTaskbarOnMaximize
        {
            get { return (bool) GetValue(IgnoreTaskbarOnMaximizeProperty); }
            set { SetValue(IgnoreTaskbarOnMaximizeProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the brush used for the titlebar's foreground.
        /// </summary>
        public Brush TitleForeground
        {
            get { return (Brush) GetValue(TitleForegroundProperty); }
            set { SetValue(TitleForegroundProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the title content template to show a custom title.
        /// </summary>
        public DataTemplate TitleTemplate
        {
            get { return (DataTemplate) GetValue(TitleTemplateProperty); }
            set { SetValue(TitleTemplateProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the icon content template to show a custom icon.
        /// </summary>
        public DataTemplate IconTemplate
        {
            get { return (DataTemplate) GetValue(IconTemplateProperty); }
            set { SetValue(IconTemplateProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the brush used for the Window's non-active title bar.
        /// </summary>
        public Brush NonActiveWindowTitleBrush
        {
            get { return (Brush) GetValue(NonActiveWindowTitleBrushProperty); }
            set { SetValue(NonActiveWindowTitleBrushProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the brush used for the Window's non-active border.
        /// </summary>
        public Brush NonActiveBorderBrush
        {
            get { return (Brush) GetValue(NonActiveBorderBrushProperty); }
            set { SetValue(NonActiveBorderBrushProperty, value); }
        }

        /// <summary>
        ///     Gets or sets the title bar visibility.
        /// </summary>
        /// <value>
        ///     The title bar visibility.
        /// </value>
        public Visibility TitleBarVisibility
        {
            get { return (Visibility) GetValue(TitleBarVisibilityProperty); }
            set { SetValue(TitleBarVisibilityProperty, value); }
        }


        /// <summary>
        /// Gets or sets the window title visibility.
        /// </summary>
        /// <value>
        /// The window title visibility.
        /// </value>
        public Visibility WindowTitleVisibility
        {
            get { return (Visibility)GetValue(WindowTitleVisibilityProperty); }
            set { SetValue(WindowTitleVisibilityProperty, value); }
        }

        /// <summary>
        ///     Get/sets whether the titlebar icon is visible or not.
        /// </summary>
        public Visibility TitleIconVisibility
        {
            get { return (Visibility) GetValue(TitleIconVisibilityProperty); }
            set { SetValue(TitleIconVisibilityProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the TitleBar's height.
        /// </summary>
        public int TitlebarHeight
        {
            get { return (int) GetValue(TitlebarHeightProperty); }
            set { SetValue(TitlebarHeightProperty, value); }
        }


        /// <summary>
        ///     Gets or sets the icon on title bar visibility.
        /// </summary>
        /// <value>
        ///     The icon on title bar visibility.
        /// </value>
        public Visibility IconOnTitleBarVisibility
        {
            get { return (Visibility) GetValue(IconOnTitleBarVisibilityProperty); }
            set { SetValue(IconOnTitleBarVisibilityProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the left window commands that hosts the user commands.
        /// </summary>
        public WindowCommands LeftWindowCommands
        {
            get { return (WindowCommands) GetValue(LeftWindowCommandsProperty); }
            set { SetValue(LeftWindowCommandsProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the right window commands that hosts the user commands.
        /// </summary>
        public WindowCommands RightWindowCommands
        {
            get { return (WindowCommands) GetValue(RightWindowCommandsProperty); }
            set { SetValue(RightWindowCommandsProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the FlyoutContainer that hosts the window's flyouts.
        /// </summary>
        public FlyoutContainer Flyouts
        {
            get { return (FlyoutContainer) GetValue(FlyoutsProperty); }
            set { SetValue(FlyoutsProperty, value); }
        }

        /// <summary>
        ///     Gets/sets whether the window's entrance transition animation is enabled.
        /// </summary>
        public bool IsWindowTransitionsEnabled
        {
            get { return (bool) GetValue(IsWindowTransitionsEnabledProperty); }
            set { SetValue(IsWindowTransitionsEnabledProperty, value); }
        }

        /// <summary>
        ///     Gets or sets the minimum button visibility.
        /// </summary>
        /// <value>
        ///     The minimum button visibility.
        /// </value>
        public Visibility MinButtonVisibility
        {
            get { return (Visibility) GetValue(MinButtonVisibilityProperty); }
            set { SetValue(MinButtonVisibilityProperty, value); }
        }


        /// <summary>
        ///     Gets or sets the maximum restore button visibility.
        /// </summary>
        /// <value>
        ///     The maximum restore button visibility.
        /// </value>
        public Visibility MaxRestoreButtonVisibility
        {
            get { return (Visibility) GetValue(MaxRestoreButtonVisibilityProperty); }
            set { SetValue(MaxRestoreButtonVisibilityProperty, value); }
        }


        /// <summary>
        ///     Gets or sets the close button visibility.
        /// </summary>
        /// <value>
        ///     The close button visibility.
        /// </value>
        public Visibility CloseButtonVisibility
        {
            get { return (Visibility) GetValue(CloseButtonVisibilityProperty); }
            set { SetValue(CloseButtonVisibilityProperty, value); }
        }

        /// <summary>
        ///     Gets/sets the visibility whether the Window Commands will show on top of a Flyout with it's position set to Top or
        ///     Right.
        /// </summary>
        public bool ShowWindowCommandsAlwaysOnTop
        {
            get { return (bool) GetValue(WindowCommandsVisibilityProperty); }
            set { SetValue(WindowCommandsVisibilityProperty, value); }
        }

        /// <summary>
        ///     CleanWindow sets this so it has the correct default window commands brush
        /// </summary>
        public SolidColorBrush OverrideDefaultWindowCommandsBrush
        {
            get { return (SolidColorBrush) GetValue(OverrideDefaultWindowCommandsBrushProperty); }
            set { SetValue(OverrideDefaultWindowCommandsBrushProperty, value); }
        }

        /// <summary>
        ///     Gets/sets whether the WindowStyle is None or not.
        /// </summary>
        public bool UseNoneWindowStyle
        {
            get { return (bool) GetValue(UseNoneWindowStyleProperty); }
            set { SetValue(UseNoneWindowStyleProperty, value); }
        }

        /// <summary>
        ///     Gets/sets if the TitleBar's text is automatically capitalized.
        /// </summary>
        public bool TitleCaps
        {
            get { return (bool) GetValue(TitleCapsProperty); }
            set { SetValue(TitleCapsProperty, value); }
        }

        /// <summary>
        /// Gets/sets whether the Window Commands will show on top of a Flyout with it's position set to Top or Right.
        /// </summary>
        public bool ShowWindowCommandsOnTop
        {
            get { return (bool) GetValue(ShowWindowCommandsOnTopProperty); }
            set { SetValue(ShowWindowCommandsOnTopProperty, value); }
        }

        #endregion

        #region  - Methoden (öffentlich) -

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            if (LeftWindowCommands == null)
            {
                LeftWindowCommands = new WindowCommands();
            }
            if (RightWindowCommands == null)
            {
                RightWindowCommands = new WindowCommands();
            }

            LeftWindowCommandsPresenter = GetTemplateChild(PART_LeftWindowCommands) as ContentPresenter;
            RightWindowCommandsPresenter = GetTemplateChild(PART_RightWindowCommands) as ContentPresenter;
            WindowButtonCommands = GetTemplateChild(PART_WindowButtonCommands) as WindowButtonCommands;

            this.OverlayBox = GetTemplateChild(PART_OverlayBox) as Grid;
            //metroDialogContainer = GetTemplateChild(PART_MetroDialogContainer) as Grid;
            flyoutModal = GetTemplateChild(PART_FlyoutModal) as Rectangle;
            flyoutModal.PreviewMouseDown += FlyoutsPreviewMouseDown;
            PreviewMouseDown += FlyoutsPreviewMouseDown;

            icon = GetTemplateChild(PART_Icon) as UIElement;
            titleBar = GetTemplateChild(PART_TitleBar) as UIElement;
            titleBarBackground = GetTemplateChild(PART_WindowTitleBackground) as UIElement;

            ToggleVisibiltyForAllTitleElements((TitlebarHeight > 0) ? Visibility.Visible : Visibility.Collapsed);
        }

        /// <summary>
        ///     Begins to show the MetroWindow's overlay effect.
        /// </summary>
        /// <returns>A task representing the process.</returns>
        public Task ShowOverlayAsync()
        {
            if (IsOverlayVisible() && overlayStoryboard == null)
            {
                return new Task(() => { }); //No Task.FromResult in .NET 4.
            }

            Dispatcher.VerifyAccess();

            this.OverlayBox.Visibility = Visibility.Visible;

            var tcs = new TaskCompletionSource<object>();

            var sb = (Storyboard) Template.Resources["OverlayFastSemiFadeIn"];

            sb = sb.Clone();

            EventHandler completionHandler = null;
            completionHandler = (sender, args) =>
            {
                sb.Completed -= completionHandler;

                if (Equals(overlayStoryboard, sb))
                {
                    overlayStoryboard = null;
                }

                tcs.TrySetResult(null);
            };

            sb.Completed += completionHandler;

            this.OverlayBox.BeginStoryboard(sb);

            overlayStoryboard = sb;

            return tcs.Task;
        }

        /// <summary>
        ///     Begins to hide the MetroWindow's overlay effect.
        /// </summary>
        /// <returns>A task representing the process.</returns>
        public Task HideOverlayAsync()
        {
            if (this.OverlayBox.Visibility == Visibility.Visible && this.OverlayBox.Opacity == 0.0)
                return new Task(() => { }); //No Task.FromResult in .NET 4.

            Dispatcher.VerifyAccess();

            var tcs = new TaskCompletionSource<object>();

            var sb = (Storyboard) Template.Resources["OverlayFastSemiFadeOut"];

            sb = sb.Clone();

            EventHandler completionHandler = null;
            completionHandler = (sender, args) =>
            {
                sb.Completed -= completionHandler;

                if (overlayStoryboard == sb)
                {
                    this.OverlayBox.Visibility = Visibility.Hidden;
                    overlayStoryboard = null;
                }

                tcs.TrySetResult(null);
            };

            sb.Completed += completionHandler;

            this.OverlayBox.BeginStoryboard(sb);

            overlayStoryboard = sb;

            return tcs.Task;
        }

        public bool IsOverlayVisible()
        {
            return this.OverlayBox.Visibility == Visibility.Visible && this.OverlayBox.Opacity >= 0.7;
        }

        public void ShowOverlay()
        {
            this.OverlayBox.Visibility = Visibility.Visible;
            //overlayBox.Opacity = 0.7;
            this.OverlayBox.SetCurrentValue(OpacityProperty, 0.7);
        }

        public void HideOverlay()
        {
            //overlayBox.Opacity = 0.0;
            this.OverlayBox.SetCurrentValue(OpacityProperty, 0.0);
            this.OverlayBox.Visibility = Visibility.Hidden;
        }

        #endregion

        #region  - Methoden (privat) -

        private static void TitlebarHeightPropertyChangedCallback(DependencyObject dependencyObject,
            DependencyPropertyChangedEventArgs e)
        {
            var window = (MetroWindow) dependencyObject;
            if (e.NewValue != e.OldValue)
            {
                Visibility newVisibility = ((int) e.NewValue > 0) ? Visibility.Visible : Visibility.Collapsed;
                window.ToggleVisibiltyForAllTitleElements(newVisibility);
            }
        }

        private static void OnTitleBarVisibilityPropertyChangedCallback(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != e.OldValue)
            {
                ((MetroWindow) d).ToggleVisibiltyForAllTitleElements((Visibility) e.NewValue);
            }
        }

        private static object OnTitleBarVisibilityCoerceValueCallback(DependencyObject d, object value)
        {
            // if UseNoneWindowStyle = true no title bar should be shown
            if (((MetroWindow) d).UseNoneWindowStyle)
            {
                return Visibility.Collapsed;
            }
            return value;
        }

        private static void ShowSystemMenuPhysicalCoordinates(Window window, Point physicalScreenLocation)
        {
            if (window == null) return;

            IntPtr hwnd = new WindowInteropHelper(window).Handle;
            if (hwnd == IntPtr.Zero || !UnsafeNativeMethods.IsWindow(hwnd))
            {
                return;
            }

            IntPtr hmenu = UnsafeNativeMethods.GetSystemMenu(hwnd, false);

            uint cmd = UnsafeNativeMethods.TrackPopupMenuEx(hmenu, Constants.TPM_LEFTBUTTON | Constants.TPM_RETURNCMD,
                (int) physicalScreenLocation.X, (int) physicalScreenLocation.Y, hwnd, IntPtr.Zero);
            if (0 != cmd)
            {
                UnsafeNativeMethods.PostMessage(hwnd, Constants.SYSCOMMAND, new IntPtr(cmd), IntPtr.Zero);
            }
        }

        private static void OnUseNoneWindowStylePropertyChangedCallback(DependencyObject d,
            DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue != e.OldValue)
            {
                // if UseNoneWindowStyle = true no title bar should be shown
                if ((bool) e.NewValue)
                {
                    ((MetroWindow) d).TitleBarVisibility = Visibility.Collapsed;
                }
            }
        }

        internal void HandleFlyoutStatusChange(Flyout flyout, IEnumerable<Flyout> visibleFlyouts)
        {
            //checks a recently opened flyout's position.
            if (flyout.Position == Position.Left || flyout.Position == Position.Right || flyout.Position == Position.Top)
            {
                //get it's zindex
                int zIndex = flyout.IsFlyoutVisible ? Panel.GetZIndex(flyout) + 3 : visibleFlyouts.Count() + 2;

                //if ShowWindowCommandsOnTop is true, set the window commands' and icon zindex to a number that is higher than the flyout's. 
                if (icon != null)
                {
                    icon.SetValue(Panel.ZIndexProperty, ShowWindowCommandsOnTop ? zIndex : 1);
                }
                if (LeftWindowCommandsPresenter != null)
                {
                    LeftWindowCommandsPresenter.SetValue(Panel.ZIndexProperty, ShowWindowCommandsOnTop ? zIndex : 1);
                }
                if (RightWindowCommandsPresenter != null)
                {
                    RightWindowCommandsPresenter.SetValue(Panel.ZIndexProperty, ShowWindowCommandsOnTop ? zIndex : 1);
                }
                if (WindowButtonCommands != null)
                {
                    WindowButtonCommands.SetValue(Panel.ZIndexProperty, ShowWindowCommandsOnTop ? zIndex : 1);
                }

                this.HandleWindowCommandsForFlyouts(visibleFlyouts);
            }

            flyoutModal.Visibility = visibleFlyouts.Any(x => x.IsModal) ? Visibility.Visible : Visibility.Hidden;

            RaiseEvent(new FlyoutStatusChangedRoutedEventArgs(FlyoutsStatusChangedEvent, this)
            {
                ChangedFlyout = flyout
            });
        }

        internal T GetPart<T>(string name) where T : DependencyObject
        {
            return GetTemplateChild(name) as T;
        }

        protected override void OnStateChanged(EventArgs e)
        {
            if (WindowButtonCommands != null && !UseNoneWindowStyle)
            {
                WindowButtonCommands.RefreshMaximiseIconState();
            }

            base.OnStateChanged(e);
        }

        protected void TitleBarMouseUp(object sender, MouseButtonEventArgs e)
        {
            Point mousePosition = e.GetPosition(this);
            bool isIconClick = IsClickOnWindowIcon(mousePosition);
            if (e.ChangedButton == MouseButton.Right && !isIconClick && TitlebarHeight > 0)
            {
                ShowSystemMenuPhysicalCoordinates(this, PointToScreen(mousePosition));
            }
            isDragging = false;
        }

        protected void TitleBarMouseDown(object sender, MouseButtonEventArgs e)
        {
            Point mousePosition = e.GetPosition(this);
            bool isIconClick = IsClickOnWindowIcon(mousePosition);

            if (e.ChangedButton == MouseButton.Left)
            {
                if (isIconClick && TitlebarHeight > 0)
                {
                    TitleBarIconMouseDown(sender, e);
                }
                else if (!UseNoneWindowStyle)
                {
                    TitleBarBorderMouseDown(sender, e);
                }
            }
        }

        protected void TitleBarIconMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Left)
            {
                if (e.ClickCount == 2)
                {
                    Close();
                }
                else
                {
                    ShowSystemMenuPhysicalCoordinates(this, PointToScreen(new Point(0, TitlebarHeight)));
                }
            }
        }

        protected void TitleBarIconMouseUp(object sender, MouseButtonEventArgs e)
        {
            isDragging = false;
        }

        /// <summary>
        ///     Toggles the visibilty for all title elements.
        /// </summary>
        private void ToggleVisibiltyForAllTitleElements(Visibility visibility)
        {
            Visibility newVisibility = (visibility == Visibility.Visible && TitleBarVisibility == Visibility.Visible)
                ? Visibility.Visible
                : Visibility.Collapsed;

            if (icon != null)
            {
                icon.Visibility = (newVisibility == Visibility.Visible && TitleIconVisibility == Visibility.Visible)
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            }

            if (titleBar != null)
            {
                titleBar.Visibility = (newVisibility == Visibility.Visible && WindowTitleVisibility == Visibility.Visible)
                    ? Visibility.Visible
                    : Visibility.Collapsed;
            }

            if (titleBarBackground != null)
            {
                titleBarBackground.Visibility = newVisibility;
            }
            if (LeftWindowCommandsPresenter != null)
            {
                LeftWindowCommandsPresenter.Visibility = newVisibility;
            }
            if (RightWindowCommandsPresenter != null)
            {
                RightWindowCommandsPresenter.Visibility = newVisibility;
            }
            if (WindowButtonCommands != null)
            {
                WindowButtonCommands.Visibility = newVisibility;
            }

            SetWindowEvents();
        }

        private bool IsClickOnWindowIcon(Point mousePosition)
        {
            return icon == null && IconOnTitleBarVisibility == Visibility.Visible &&
                   mousePosition.X <= TitlebarHeight &&
                   mousePosition.Y <= TitlebarHeight;
        }

        private void TitleBarMouseMove(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed)
            {
                isDragging = false;
            }

            if (isDragging
                && WindowState == WindowState.Maximized
                && ResizeMode != ResizeMode.NoResize)
            {
                // Calculating correct left coordinate for multi-screen system.
                Point mouseAbsolute = PointToScreen(Mouse.GetPosition(this));
                double width = RestoreBounds.Width;
                double left = mouseAbsolute.X - width/2;

                // Check if the mouse is at the top of the screen if TitleBar is not visible
                if (titleBar.Visibility != Visibility.Visible && mouseAbsolute.Y > TitlebarHeight)
                {
                    return;
                }

                // Aligning window's position to fit the screen.
                double virtualScreenWidth = SystemParameters.VirtualScreenWidth;
                left = left + width > virtualScreenWidth ? virtualScreenWidth - width : left;

                Point mousePosition = e.MouseDevice.GetPosition(this);

                // When dragging the window down at the very top of the border,
                // move the window a bit upwards to avoid showing the resize handle as soon as the mouse button is released
                Top = mousePosition.Y < 5 ? -5 : mouseAbsolute.Y - mousePosition.Y;
                Left = left;

                // Restore window to normal state.
                WindowState = WindowState.Normal;
            }
        }

        private void TitleBarBorderMouseDown(object sender, MouseButtonEventArgs e)
        {
            IntPtr windowHandle = new WindowInteropHelper(this).Handle;
            UnsafeNativeMethods.ReleaseCapture();

            Point mPoint = Mouse.GetPosition(this);

            Point wpfPoint = PointToScreen(mPoint);
            short x = Convert.ToInt16(wpfPoint.X);
            short y = Convert.ToInt16(wpfPoint.Y);
            int lParam = x | (y << 16);
            UnsafeNativeMethods.SendMessage(windowHandle, Constants.WM_NCLBUTTONDOWN, Constants.HT_CAPTION, lParam);

            if (e.ClickCount == 2 &&
                (ResizeMode == ResizeMode.CanResizeWithGrip || ResizeMode == ResizeMode.CanResize) &&
                mPoint.Y <= TitlebarHeight && TitlebarHeight > 0)
            {
                if (WindowState == WindowState.Maximized)
                {
                    SystemCommands.RestoreWindow(this);
                }
                else
                {
                    SystemCommands.MaximizeWindow(this);
                }
            }
        }

        private void MetroWindow_SizeChanged(object sender, RoutedEventArgs e)
        {
            // this all works only for CleanWindow style
            var titleBarGrid = titleBar as Grid;
            var titleBarLabel = titleBarGrid.Children[0] as Label;
            var titleControl = titleBarLabel.Content as ContentControl;
            var iconContentControl = icon as ContentControl;

            // Half of this MetroWindow
            double halfDistance = Width/2;
            // Distance between center and left/right
            double distanceToCenter = titleControl.ActualWidth/2;
            // Distance between right edge from LeftWindowCommands to left window side
            double distanceFromLeft = iconContentControl.ActualWidth + LeftWindowCommands.ActualWidth;
            // Distance between left edge from RightWindowCommands to right window side
            double distanceFromRight = WindowButtonCommands.ActualWidth + RightWindowCommands.ActualWidth;
            // Margin
            const double horizontalMargin = 5.0;

            if ((distanceFromLeft + distanceToCenter + horizontalMargin < halfDistance) &&
                (distanceFromRight + distanceToCenter + horizontalMargin < halfDistance))
            {
                Grid.SetColumn(titleBarGrid, 0);
                Grid.SetColumnSpan(titleBarGrid, 5);
            }
            else
            {
                Grid.SetColumn(titleBarGrid, 2);
                Grid.SetColumnSpan(titleBarGrid, 1);
            }
        }

        private void SetWindowEvents()
        {
            // clear all event handlers first:

            if (titleBarBackground != null)
            {
                titleBarBackground.MouseDown -= TitleBarMouseDown;
                titleBarBackground.MouseUp -= TitleBarMouseUp;
                titleBarBackground.MouseMove -= TitleBarMouseMove;
            }

            if (titleBar != null)
            {
                titleBar.MouseDown -= TitleBarMouseDown;
                titleBar.MouseUp -= TitleBarMouseUp;
                titleBar.MouseMove -= TitleBarMouseMove;
            }

            if (icon != null)
            {
                icon.MouseDown -= TitleBarIconMouseDown;
                icon.MouseUp -= TitleBarIconMouseUp;
            }

            MouseDown -= TitleBarMouseDown;
            MouseUp -= TitleBarMouseUp;
            MouseMove -= TitleBarMouseMove;

            SizeChanged -= MetroWindow_SizeChanged;

            if (icon != null && icon.Visibility == Visibility.Visible)
            {
                icon.MouseDown += TitleBarIconMouseDown;
                icon.MouseUp += TitleBarIconMouseUp;
            }

            // handle mouse events if title template use HorizontalAlignment != Center
            if (titleBarBackground != null && titleBarBackground.Visibility == Visibility.Visible)
            {
                titleBarBackground.MouseDown += TitleBarMouseDown;
                titleBarBackground.MouseUp += TitleBarMouseUp;
                titleBarBackground.MouseMove += TitleBarMouseMove;
            }

            if (titleBar != null && titleBar.Visibility == Visibility.Visible)
            {
                titleBar.MouseDown += TitleBarMouseDown;
                titleBar.MouseUp += TitleBarMouseUp;
                titleBar.MouseMove += TitleBarMouseMove;

                if (titleBar.GetType() == typeof (Grid))
                {
                    SizeChanged += MetroWindow_SizeChanged;
                }
            }
            else
            {
                MouseDown += TitleBarMouseDown;
                MouseUp += TitleBarMouseUp;
                MouseMove += TitleBarMouseMove;
            }
        }

        private void SetWindowElementsVisibilityByWindowStyle(bool useNoneWindowStyle)
        {
            Visibility windowElementVisibility = (useNoneWindowStyle)
                ? Visibility.Collapsed
                : Visibility.Visible;
            CloseButtonVisibility = windowElementVisibility;
            MaxRestoreButtonVisibility = windowElementVisibility;
            MinButtonVisibility = windowElementVisibility;

            if (LeftWindowCommandsPresenter != null)
            {
                LeftWindowCommandsPresenter.Visibility = windowElementVisibility;
            }
            if (RightWindowCommandsPresenter != null)
            {
                RightWindowCommandsPresenter.Visibility = windowElementVisibility;
            }
        }


        private void MetroWindow_Loaded(object sender, RoutedEventArgs e)
        {
            if (IsWindowTransitionsEnabled)
            {
                VisualStateManager.GoToState(this, "AfterLoaded", true);
            }

            SetWindowElementsVisibilityByWindowStyle(UseNoneWindowStyle);

            if (Flyouts == null)
            {
                Flyouts = new FlyoutContainer();
            }

            this.ResetAllWindowCommandsBrush();

            ThemeManager.IsThemeChanged += ThemeManagerOnIsThemeChanged;
            Unloaded += (o, args) => ThemeManager.IsThemeChanged -= ThemeManagerOnIsThemeChanged;
        }

        private void FlyoutsPreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement element = (e.OriginalSource as FrameworkElement);
            if (element != null && element.TryFindParent<Flyout>() != null)
            {
                return;
            }

            if (Flyouts.OverrideExternalCloseButton == null)
            {
                foreach (
                    Flyout flyout in
                        Flyouts.GetFlyouts()
                            .Where(
                                x =>
                                    x.IsFlyoutVisible && x.ExternalCloseButton == e.ChangedButton &&
                                    (!x.IsPinned || Flyouts.OverrideIsPinned)))
                {
                    flyout.ElementSize = ElementSize.Minimized;
                    e.Handled = true;
                }
            }

            else if (Flyouts.OverrideExternalCloseButton == e.ChangedButton)
            {
                foreach (
                    Flyout flyout in
                        Flyouts.GetFlyouts().Where(x => x.IsFlyoutVisible && (!x.IsPinned || Flyouts.OverrideIsPinned)))
                {
                    flyout.ElementSize = ElementSize.Minimized;
                    e.Handled = true;
                }
            }
        }

        private void ThemeManagerOnIsThemeChanged(object sender, ThemeChangedEventArgs e)
        {
            if (e.Accent != null)
            {
                List<Flyout> flyouts = Flyouts.GetFlyouts().ToList();
                // since we disabled the ThemeManager OnThemeChanged part, we must change all children flyouts too
                // e.g if the FlyoutsControl is hosted in a UserControl
                List<FlyoutContainer> allChildFlyouts =
                    (Content as DependencyObject).FindChildren<FlyoutContainer>(true).ToList();
                if (allChildFlyouts.Any())
                {
                    flyouts.AddRange(allChildFlyouts.SelectMany(flyoutsControl => flyoutsControl.GetFlyouts()));
                }

                if (!flyouts.Any())
                {
                    // we must update the window command brushes!!!
                    this.ResetAllWindowCommandsBrush();
                    return;
                }

                foreach (Flyout flyout in flyouts)
                {
                    flyout.ChangeFlyoutTheme(e.Accent, e.AppTheme);
                }
                this.HandleWindowCommandsForFlyouts(flyouts);
            }
        }

        #endregion

        private const string PART_FlyoutModal = "PART_FlyoutModal";
        private const string PART_OverlayBox = "PART_OverlayBox";
        private const string PART_Icon = "PART_Icon";
        private const string PART_TitleBar = "PART_TitleBar";
        private const string PART_WindowTitleBackground = "PART_WindowTitleBackground";
        private const string PART_LeftWindowCommands = "PART_LeftWindowCommands";
        private const string PART_RightWindowCommands = "PART_RightWindowCommands";
        private const string PART_WindowButtonCommands = "PART_WindowButtonCommands";
    }
}